package br.eng.eliseu.gwt.gestaoWeb.server.atualizacao;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import javax.persistence.EntityManager;
import javax.sql.rowset.serial.SerialBlob;
import javax.sql.rowset.serial.SerialClob;

import org.apache.commons.lang3.ArrayUtils;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import br.eng.eliseu.gwt.gestaoWeb.server.atualizacao.util.Tabela;

import com.google.inject.Inject;

public class TabelaPadraoXMLHandler extends DefaultHandler {

	@Inject 
	EntityManager em;

	private String tabela;
	private List<Coluna> colunas = new ArrayList<TabelaPadraoXMLHandler.Coluna>();
	
	private int campoAtual;
	
//	private SituacaoAtualizacaoTabelaPadraoTO to;
	private Connection connection;
	private PreparedStatement stmt;
	
	private boolean valorSetadoStmt = false;
	private boolean heValor = false;
	
	private static final int batchSize = 1;
	private int registroAtual = 0;
	
	final String branco = "";
	
	public TabelaPadraoXMLHandler(Connection connection/*, SituacaoAtualizacaoTabelaPadraoTO to*/) {
		super();
		this.connection = connection;
		this.registroAtual = 0;
//		this.to = to;
	}
	
	@Override
	public void startDocument() throws SAXException {
		super.startDocument();
	}
	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
		if (qName.equals("colunas") || qName.equals("dados")){
			return;
		}
		if (qName.equals("tabelapadrao")){
			trataTabelaPadrao(attributes);
			return;
		} 
		if (qName.equals("coluna")){
			trataColuna(attributes);
			return;
		}
		if (qName.equals("linha")){
			return;
		}
		if (qName.startsWith("c")){
			trataCampo(Integer.parseInt(qName.substring(1)));
			return;
		}
	}
	
	private void trataFimColunas() throws SAXException {
		try{
			String q = "Insert into " + tabela + "( "+getColunas()+" ) values ( "+getStmtValues()+" )";
			stmt = connection.prepareStatement(q);
			
		} catch (Exception e){
			throw new SAXException("Erro criando PreparedStatment: ", e);
		}
	}

	private String getStmtValues() {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < colunas.size(); i++) {
			if (sb.length()>0){
				sb.append(", ");
			}
			sb.append("?");
		}
		return sb.toString();
	}

	private String getColunas() {
//		Collections.sort(colunas);
		StringBuffer sb = new StringBuffer();
		for (Coluna coluna : colunas) {
			if (sb.length()>0){
				sb.append(", ");
			}
			sb.append(coluna.getNome());
		}
		return sb.toString();
	}

	@Override
	public void characters(char[] buffer, int inicio, int tamanho) throws SAXException {
//		super.characters(buffer, inicio, tamanho);
		char[] conteudo = ArrayUtils.subarray(buffer, inicio, inicio+tamanho);
		if (conteudo==null) return ;
//		boolean achou = false;
//		for(int i=0;i<conteudo.length;i++){
//			if((int)conteudo[1]==9 || (int)conteudo[i]==10 || (int)conteudo[i]==13 ){
//				achou = true;
//				break;
//			}
//		}
		String sValor = new String(conteudo);
		sValor = sValor.replaceAll("\n", "");
		sValor = sValor.replaceAll("\t", "");
		sValor = sValor.trim();
//		if (achou && "".equals(sValor)) return;
		if (!heValor && "".equals(sValor)) return;
		Coluna coluna = getColunaPorCodigo(campoAtual);
		if (coluna==null) return;
		Integer tipo = coluna.getTipo();
		valorSetadoStmt = true;
		try{
			if( tipo.equals(Tabela.TIPO_AUTOINCREMENT ) || tipo.equals(Tabela.TIPO_INTEGER ) || tipo.equals(Tabela.TIPO_SMALLINT ) ){
				if(sValor.equals(branco)){
					stmt.setObject(coluna.getPosicao(), null);
				} else {
					stmt.setInt(coluna.getPosicao(), Integer.valueOf(sValor));
				}
			} else if( tipo.equals(Tabela.TIPO_DECIMAL )){
				if(sValor.equals(branco)){
					stmt.setObject(coluna.getPosicao(), null);
				} else { 
					stmt.setBigDecimal(coluna.getPosicao(), new BigDecimal(sValor));
				}
			} else if( tipo.equals(Tabela.TIPO_CHAR ) || tipo.equals(Tabela.TIPO_VARCHAR2 ) ) {
				stmt.setString(coluna.getPosicao(), sValor);
			} else if( tipo.equals(Tabela.TIPO_BLOBJAVA )) {
				if(sValor.equals(branco)){
					stmt.setObject(coluna.getPosicao(), null);
				} else {
					stmt.setBlob(coluna.getPosicao(), new SerialBlob(sValor.getBytes()));
				}
			} else if( tipo.equals(Tabela.TIPO_CLOB ) ) {
				if(sValor.equals(branco)){
					stmt.setObject(coluna.getPosicao(), null);
				} else {
					stmt.setClob(coluna.getPosicao(), new SerialClob(sValor.toCharArray()));
				}
			} else if( tipo.equals(Tabela.TIPO_DATE ) ) {
				if(sValor.equals(branco)){
					stmt.setObject(coluna.getPosicao(), null);
				} else {
					Calendar dt = Calendar.getInstance();
					dt.set(Integer.valueOf(sValor.substring(0,4)), 
							Integer.valueOf(sValor.substring(5,7)), 
							Integer.valueOf(sValor.substring(8,10)), 
							Integer.valueOf(sValor.substring(11,13)),
							Integer.valueOf(sValor.substring(14,16)),
							Integer.valueOf(sValor.substring(17,19)));
					stmt.setDate(coluna.getPosicao(), new Date(dt.getTime().getTime()));
				}
			} else {
				if(sValor.equals(branco)){
					stmt.setObject(coluna.getPosicao(), null);
				} else {
					stmt.setString(coluna.getPosicao(), sValor);
				}
			}
		} catch (Exception e){
			throw new SAXException("Erro ao obter dados.", e);
		}
	}
	
	private Coluna getColunaPorCodigo(int i) {
		for (Coluna coluna : colunas) {
			if (coluna.getCodigo().equals(i)){
				return coluna;
			}
		}
		return null;
	}

	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		try{
			if (qName.equals("coluna") || qName.equals("dados")){
				return;
			}
			if (qName.equals("linha")){
				stmt.addBatch();
				registroAtual++;
				if (registroAtual%batchSize==0){
					stmt.executeBatch();
//					stmt.clearBatch();
				}
				return;
			}
			if (qName.equals("colunas")){
				trataFimColunas();
				return;
			}
			if (qName.startsWith("c")){
				heValor = false;
				if (!valorSetadoStmt){
					stmt.setObject(getColunaPorCodigo(campoAtual).getPosicao(), null);
				}
			}
		} catch (SQLException e){
			System.out.println(registroAtual+":"+e.getMessage());
		} catch (Exception e){
			throw new SAXException("Erro ao executar o insert.", e);
		}
	}
	
	@Override
	public void endDocument() throws SAXException {
		if (stmt!=null){
			try {
				if (registroAtual%batchSize!=0){
					stmt.executeBatch();
				}
				stmt.close();
			} catch (SQLException e){
				System.out.println(e.getMessage());
			} catch (Exception e) {
				System.out.println(e.getMessage());
				throw new SAXException("Erro ao fechar o PreparedStatment", e);
			}
		}
	}
	
	private void trataCampo(Integer campoId) {
		heValor = true;
		valorSetadoStmt = false;
		campoAtual = campoId;
	}
	
	private void trataColuna(Attributes attributes) {
		colunas.add(new Coluna(attributes.getValue("nome"), new Integer(attributes.getValue("tipo")), new Integer(attributes.getValue("codigo")), colunas.size()+1));
	}
	
	private void trataTabelaPadrao(Attributes attributes) {
		tabela = attributes.getValue("tabela");
	}
	
	class Coluna implements Comparable<Coluna>{
		private String nome;
		private Integer tipo;
		private Integer codigo;
		private Integer posicao;
		
		public Coluna(String nome, Integer tipo, Integer codigo, Integer posicao) {
			this.nome = nome;
			this.tipo = tipo;
			this.codigo = codigo;
			this.setPosicao(posicao);
		}
		
		public String getNome() {
			return nome;
		}
		public void setNome(String nome) {
			this.nome = nome;
		}
		public Integer getTipo() {
			return tipo;
		}
		public void setTipo(Integer tipo) {
			this.tipo = tipo;
		}
		public Integer getCodigo() {
			return codigo;
		}
		public void setCodigo(Integer codigo) {
			this.codigo = codigo;
		}

		public int compareTo(Coluna o) {
			return this.getCodigo().compareTo(o.getCodigo());
		}

		public void setPosicao(Integer posicao) {
			this.posicao = posicao;
		}

		public Integer getPosicao() {
			return posicao;
		}
		
	}
}
